package com.google.code.stalkernet.example.ui;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

import com.google.code.stalkernet.reactor.Reactor;
import com.google.code.stalkernet.server.Server;
import com.google.code.stalkernet.server.event.ServerEvent;
import com.google.code.stalkernet.server.event.ServerListener;

public class ServerTableModel extends AbstractTableModel
{
    private final List <SocketAddress> socketAddresses = new ArrayList <> ();

    private final MyServerListener myServerListener = new MyServerListener ();

    private final Reactor reactor;
    private final Server server;

    public ServerTableModel (Reactor reactor, Server server)
    {
        if (reactor == null)
            throw new IllegalArgumentException ("Reactor is null");

        if (server == null)
            throw new IllegalArgumentException ("Server is null");

        this.reactor = reactor;
        this.server = server;

        reactor.invoke (new DumpSocketAddressesAction ());
    }

    public SocketAddress getSocketAddressAt (int index)
    {
        return socketAddresses.get (index);
    }

    @Override
    public int getRowCount ()
    {
        return socketAddresses.size ();
    }

    @Override
    public int getColumnCount ()
    {
        return 2;
    }

    @Override
    public Object getValueAt (int rowIndex, int columnIndex)
    {
        InetSocketAddress inetSocketAddress =
            (InetSocketAddress)socketAddresses.get (rowIndex);

        switch (columnIndex)
        {
        case 0:
            return inetSocketAddress.getAddress ();
        case 1:
            return Integer.valueOf (inetSocketAddress.getPort ());
        default:
            throw new IllegalArgumentException (
                "Invalid column index: " + columnIndex);
        }
    }

    @Override
    public String getColumnName (int column)
    {
        switch (column)
        {
        case 0:
            return "Address";
        case 1:
            return "Port";
        default:
            throw new IllegalArgumentException (
                "Invalid columnindex: " + column);
        }
    }

    private class MyServerListener implements ServerListener
    {
        @Override
        public void socketAddressAdded (ServerEvent serverEvent)
        {
            if (serverEvent == null)
                throw new IllegalArgumentException ("Server event is null");

            final SocketAddress socketAddress = serverEvent.getSocketAddress ();

            SwingUtilities.invokeLater (
                new Runnable()
                {
                    @Override
                    public void run ()
                    {
                        int index = socketAddresses.size ();
                        socketAddresses.add (index, socketAddress);
                        fireTableRowsInserted (index, index);
                    }
                });
        }

        @Override
        public void socketAddressRemoved (ServerEvent serverEvent)
        {
            if (serverEvent == null)
                throw new IllegalArgumentException ("Server event is null");

            final SocketAddress socketAddress = serverEvent.getSocketAddress ();

            SwingUtilities.invokeLater (
                new Runnable()
                {
                    @Override
                    public void run ()
                    {
                        int index = socketAddresses.indexOf (socketAddress);
                        if (index < 0)
                            throw new IllegalStateException (
                                "Unknown socket address: " + socketAddress);

                        socketAddresses.remove (index);

                        fireTableRowsDeleted (index, index);
                    }
                });
        }
    }

    private class DumpSocketAddressesAction implements Runnable
    {
        @Override
        public void run ()
        {
            SwingUtilities.invokeLater (
                new AddSocketAddressesRunnable (
                    server.getAllSocketAddresses ()));

            server.addServerListener (myServerListener);
        }
    }

    private class AddSocketAddressesRunnable implements Runnable
    {
        private final SocketAddress [] socketAddresses;

        public AddSocketAddressesRunnable (SocketAddress [] socketAddresses)
        {
            if (socketAddresses == null)
                throw new IllegalArgumentException ("Socket addresses is null");

            this.socketAddresses = socketAddresses.clone ();
        }

        @Override
        public void run ()
        {
            int socketAddressesLength = socketAddresses.length;
            if (socketAddressesLength > 0)
            {
                int index = ServerTableModel.this.socketAddresses.size ();
                ServerTableModel.this.socketAddresses.addAll (
                    Arrays.asList (socketAddresses));
                fireTableRowsInserted (
                    index, index + socketAddressesLength - 1);
            }
        }
    }
}
